﻿namespace DynaBoard
{

    using System;
    using System.Data.Linq;
    using System.Linq;
    using System.Collections;
    using System.Collections.Generic;

    public partial class Post
    {
        public static int CountChildren(int postId)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where post.RootPost == postId
                        select post.Id).Count();
            }
        }

        public static Author Author(int postId, RequestorAddress ip)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where (post.Id == postId ||
                               post.RootPost == postId) &&
                               post.AuthorIp == ip.ToString() &&
                               post.AuthorName != null
                        select new Author()
                        {
                            Name = post.AuthorName,
                            Email = post.AuthorEmail
                        }).FirstOrDefault();
            }
        }

        public static string AuthorIpFromId(int threadStartId)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where post.Id == threadStartId
                        select post.AuthorIp).Single();
            }
        }

        public static Post FromId(int postId)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where post.Id == postId
                        select post).Single();
            }
        }
            
        public static Post[] FetchThread(int postId, int[] gottenPosts)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where (post.Id == postId ||
                              post.RootPost == postId) &&
                              !gottenPosts.Contains(post.Id)
                        orderby post.PostedUtc ascending
                        select post).ToArray();
            }
        }

        public static int StartThread
        (
            int board, 
            AuthorName authorName, 
            AuthorEmail authorEmail, 
            PostTitle title, 
            PostBody body, 
            RequestorAddress ip
        ){
            if (title == null)
            {
                throw new ArgumentNullException("subject");
            }

            if (body == null)
            {
                throw new ArgumentNullException("body");
            }

            if (ip == null)
            {
                throw new ArgumentNullException("ip");
            }

            using (var db = DynaBoardDataContext.Create)
            {
                var now = DateTime.UtcNow;

                var post = new Post()
                {
                    BoardId = board,
                    Body = body,
                    LastActivityUtc = now,
                    ParentPostId = null,
                    PostedUtc = now,
                    RootPost = null,
                    Title = title,
                    AuthorIp = ip,
                    AuthorName = authorName,
                    AuthorEmail = authorEmail
                };

                db.Post.InsertOnSubmit(post);
                db.SubmitChanges();

                return post.Id;
            }
        }

        // 

        public static int SubmitReply
        (
            int parentId, 
            PostBody body, 
            AuthorName authorName, 
            AuthorEmail authorEmail, 
            RequestorAddress ip
        ){
            if (body == null)
            {
                throw new ArgumentNullException("body");
            }

            if (ip == null)
            {
                throw new ArgumentNullException("ip");
            }

            int rootPostId;
            int newPostId;

            using (var db = DynaBoardDataContext.Create)
            {
                rootPostId = (from p in db.Post 
                              where p.Id == parentId 
                              select p.RootPost)
                              .SingleOrDefault() ?? parentId;

                var post = new Post()
                {
                    ParentPostId = parentId,
                    Body = body,
                    PostedUtc = DateTime.UtcNow,
                    RootPost = rootPostId,
                    BoardId = (from p in db.Post
                               where p.Id == rootPostId
                               select p.BoardId).Single(),
                    Title = null,
                    AuthorEmail = authorEmail,
                    AuthorName = authorName,
                    AuthorIp = ip
                };

                db.Post.InsertOnSubmit(post);
                db.SubmitChanges();

                newPostId = post.Id;
            }

            // TODO thread off; conflict resolution

            using (var db = DynaBoardDataContext.Create)
            {
                var root = (from post in db.Post
                            where post.Id == rootPostId
                            select post).Single();

                root.LastActivityUtc = DateTime.UtcNow;

                try
                {
                    db.SubmitChanges();
                }
                catch (ChangeConflictException)
                {
                    db.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges);
                    db.SubmitChanges();
                }
            }

            return newPostId;
        }

        /// <summary>
        /// Get the DateTime of the newest reply to a post.
        /// </summary>
        public static DateTime LastActive(int postId)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where post.RootPost == postId || post.Id == postId 
                        orderby post.PostedUtc descending
                        select post.PostedUtc).First();
            }
        }

        public static object[] MostActive(TimeSpan time, int count)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                // List posts by last activity with latest first

                var postTimes = (from post in db.Post
                                 where post.LastActivityUtc != null
                                 orderby post.LastActivityUtc descending
                                 select post.LastActivityUtc)
                                 .Take(100).ToArray();

                DateTime startDate = DateTime.MinValue;
                
                if (postTimes.Length >= 2)
                {
                    startDate = postTimes.Last().Value;
                }

                return (from post in db.Post
                        where post.LastActivityUtc != null &&
                              post.LastActivityUtc > startDate &&
                              post.LastActivityUtc <= DateTime.UtcNow
                        let children = (from p in db.Post
                                        where p.RootPost == post.Id
                                        select p.Id).Count()
                        orderby children descending
                        select new Thread(post, children))
                        .Take(count).ToArray();
            }
        }

        /// <summary>
        /// Get the latest posts in descending order.
        /// </summary>
        public static Thread[] FetchLatestFromBoard(int boardId, int count)
        {
            using (var db = DynaBoardDataContext.Create)
            {
                var posts = from post in db.Post
                            where post.BoardId == boardId &&
                                  post.LastActivityUtc != null
                            orderby post.LastActivityUtc descending
                            select new Thread(post);

                return posts.Take(count).ToArray();
            }
        }

        public static SearchResult TitleSearch(string searchString, int? page)
        {
            if (searchString == null)
            {
                throw new ArgumentNullException();
            }

            using (var db = DynaBoardDataContext.Create)
            {
                var all =  from post in db.Post
                           where post.RootPost == null &&
                                 post.Title.Contains(searchString)/*&&
                                 post.PostedUtc >= start &&
                                 post.LastActivityUtc <= end*/
                           orderby post.LastActivityUtc descending 
                           select new Thread(post);

                int pages = Convert.ToInt32(Math.Ceiling(all.Count() / 20D));

                if (page == null)
                {
                    return new SearchResult(1, pages, all.Take(20).ToArray());
                }
                else
                {
                    return new SearchResult(page, pages, all.AsEnumerable().Skip(20 * (page.Value - 1)).Take(20).ToArray()); 
                }
            }
        }

        public static Thread[] BodySearch(string searchString, int? page)
        {
            if (searchString == null)
            {
                throw new ArgumentNullException();
            }

            using (var db = DynaBoardDataContext.Create)
            {
                return (from post in db.Post
                        where post.Body.Contains(searchString)
                        select new Thread(post)).ToArray();
            }
        }
    }

}